package com.simen.connectandroid;

import com.simen.connectandroid.command.*;
import com.simen.connectandroid.command.pc.OpenDirCommand;
import com.simen.connectandroid.exceptions.DeviceNotReadyException;
import com.simen.connectandroid.exceptions.NoConnectedDeviceException;
import com.simen.connectandroid.model.Device;
import com.simen.connectandroid.model.DeviceIp;
import com.simen.hellobytes.DirUtil;
import com.simen.hellobytes.StringUtil;
import com.simen.hellobytes.http.IpUtil;
import com.simen.hellobytes.lang.Filter;
import com.simen.hellobytes.lang.ListUtil;
import com.simen.hellobytes.lang.TypeParseUtil;
import com.simen.hellobytes.log.Log;

import java.io.IOException;
import java.util.List;

public class Launcher {

    public static boolean DEBUG = false;

    public static void main(String[] args) {
        if (args != null && args.length % 2 == 0) {
            for (int i = 0; i < args.length; i += 2) {
                if ("-debug".equalsIgnoreCase(args[i])) {
                    Log.setDEBUG("true".equalsIgnoreCase(args[i + 1]));
                } else if ("-help".equalsIgnoreCase(args[i])) {
                    System.out.println("1，可指定设备 2，只有1台网络设备时，默认断开此设备。2，多台网络设备时，输入序号即可断开指定设备");
                    System.out.println("-connect [deviceName] 开启设备网络调试并连接设备");
                    System.out.println("-disconnect [deviceName] 断开设备连接");
                    System.out.println("-apk [devicesName] [packageName] 导出指定设备上的安装包到本机");
                    System.out.println("-topapk [devicesName] 导出指定设备当前显示App的安装包到本机");
                    //System.out.println("-push [devicesName] [filePath] 将文件复制到指定设备存储区根目录");
                    System.out.println("-debug 是否开启调试模式，可取值为 true、false");
                    System.out.println("-help 帮助");
                    return;
                }
            }
        }

        Log.setDEBUG(DEBUG);

        Launcher launcher = new Launcher(args);
        launcher.start();
    }

    private Operation operation;
    private String param0;
    private String param1;

    private Launcher(String[] args) {
        operation = args.length > 0 ? Operation.parse(args[0]) : null;
        param0 = args.length > 1 ? args[1] : null;
        param1 = args.length > 2 ? args[2] : null;
    }

    private void start() {
        DevicesCommand devicesCommand = new DevicesCommand();
        devicesCommand.exec();
        try {
            if (devicesCommand.getDeviceList().isEmpty()) {
                System.out.println("No Connected Device");
            } else {
                if (operation == Operation.DISCONNECT) {
                    //断开设备连接
                    Device device = autoChooseDevice(devicesCommand, param0, true);
                    if (device != null) disconnectDevice(device);
                } else if (operation == Operation.PULLAPK) {
                    Device device = autoChooseDevice(devicesCommand, param1 != null ? param0 : null, false);
                    if (device != null) pullApkFromDevice(device, param1 != null ? param1 : param0);
                } else if (operation == Operation.PULLTOPAPK) {
                    Device device = autoChooseDevice(devicesCommand, param0, false);
                    if (device != null) pullTopApkFromDevice(device);
                } else if (operation == Operation.PUSH) {
                    Device device = autoChooseDevice(devicesCommand, param0, false);
                    if (device != null) pushFileToDevice(device);
                } else {
                    //连接设备
                    Device device = autoChooseDevice(devicesCommand, param0, false);
                    if (device != null) connectDevice(device);
                }
            }
        } catch (DeviceNotReadyException e) {
            System.out.println(e.getMessage());
        } catch (NoConnectedDeviceException e) {
            System.out.println(e.getMessage());
        }

        System.out.println();
        System.out.println("finished");
    }

    /**
     * @param netDevice true:只过滤选择网络设备；false，过滤所有设备
     */
    private Device autoChooseDevice(DevicesCommand devicesCommand,
                                    final String deviceName,
                                    final boolean netDevice) throws NoConnectedDeviceException {
        if (netDevice) {//检查有没有连接的网络设备
            List<Device> wlanDeviceList = ListUtil.filter(devicesCommand.getDeviceList(), new Filter<Device>() {
                @Override
                public boolean filter(Device device) {
                    return device.isNetDevice();
                }
            });
            if (wlanDeviceList.isEmpty()) {
                throw new NoConnectedDeviceException("No Connected Wlan Device");
            }
        } else {//检查有没有连接的设备
            if (devicesCommand.getDeviceList().isEmpty()) {
                throw new NoConnectedDeviceException("No Connected Device");
            }
        }

        if (deviceName != null) { //指定设备
            List<Device> deviceList = ListUtil.filter(devicesCommand.getDeviceList(), new Filter<Device>() {
                @Override
                public boolean filter(Device device) {
                    if (netDevice) {
                        return device.isNetDevice() && deviceName.equalsIgnoreCase(device.getAdbDeviceName());
                    }

                    return deviceName.equalsIgnoreCase(device.getAdbDeviceName());
                }
            });
            if (deviceList.isEmpty()) {
                System.out.println("No Connected " + (netDevice ? "Wlan " : "") + "Device：" + deviceName);
                return null;
            }
            return deviceList.get(0);
        }

        //没有指定设备
        List<Device> deviceList = ListUtil.filter(devicesCommand.getDeviceList(), new Filter<Device>() {
            @Override
            public boolean filter(Device device) {
                if (netDevice) return device.isNetDevice();
                return true;
            }
        });
        if (deviceList.size() == 1) {//默认第1台设备或网络设备
            return deviceList.get(0);
        } else {
            //用户通过输入设备序号或设备名选择设备
            Device devices = requestUserChoose(deviceList);
            if (devices == null) {
                return null;
            }
            return devices;
        }
    }

    private void disconnectDevice(DevicesCommand devicesCommand) {
        if (param0 != null) {
            List<Device> wlanDeviceList = ListUtil.filter(devicesCommand.getDeviceList(), new Filter<Device>() {
                @Override
                public boolean filter(Device device) {
                    return device.isNetDevice() && param0.equalsIgnoreCase(device.getAdbDeviceName());
                }
            });
            if (wlanDeviceList.isEmpty()) {
                System.out.println("Device " + param0 + " is not Connected");
            } else {
                disconnectDevice(wlanDeviceList.get(0));
            }
            return;
        }
        List<Device> wlanDeviceList = ListUtil.filter(devicesCommand.getDeviceList(), new Filter<Device>() {
            @Override
            public boolean filter(Device device) {
                return device.isNetDevice();
            }
        });
        if (wlanDeviceList.isEmpty()) {
            System.out.println("No Connected WlanDevice");
        } else if (wlanDeviceList.size() == 1) {
            disconnectDevice(wlanDeviceList.get(0));
        } else {
            Device wlanDevice = requestUserChoose(wlanDeviceList);
            if (wlanDevice == null) {
                System.out.println("finished");
                return;
            }
            disconnectDevice(wlanDevice);
        }
    }

    /**
     * 询问用户选择设备
     *
     * @param deviceList 设备列表
     * @return 返回用户选择的设备
     */
    private Device requestUserChoose(final List<Device> deviceList) {
        ListUtil.filter(deviceList, new Filter<Device>() {
            @Override
            public boolean filter(Device device) {
                System.out.println((deviceList.indexOf(device) + 1) + "," + device.getSimpleName() + " ->status: " + device.getStatus());
                return false;
            }
        });
        System.out.print("please choose device(index(1-" + deviceList.size() + ") or name): ");
        try {
            for (int i = 0; i < 3; i++) {
                byte[] buffer = new byte[1024];
                int read = System.in.read(buffer, 0, buffer.length);
                if (read != -1) {
                    String input = new String(buffer, 0, read).trim();
                    int index = TypeParseUtil.parseInt(input, -1);
                    if (index != -1 && index < 100) {
                        //输入设备序号（index从1开始）
                        if (index > 0 && index <= deviceList.size()) {
                            return deviceList.get(index - 1);
                        } else {
                            System.out.println();
                            System.out.print("input error,please input again:");
                        }
                    } else {
                        //输入设备名
                        for (int j = 0; j < deviceList.size(); j++) {
                            if (deviceList.get(j).getAdbDeviceName().equalsIgnoreCase(input)) {
                                return deviceList.get(j);
                            }
                        }

                        System.out.println();
                        System.out.print("no device name is " + input + ",please input again:");
                    }
                }
            }
        } catch (IOException e) {
        }
        return null;
    }

    private void connectDevice(Device device) throws DeviceNotReadyException {
        System.out.println("will connect device: " + device.getAdbDeviceName());
        makeSureDeviceIsReady(device);

        GetIpCommand getIpCommand = new GetIpCommand(device);
        getIpCommand.exec();

        if (!getIpCommand.execResult()) {
            System.err.println("get device ip fail: " + getIpCommand.log());
            return;
        }

        makeSureDeviceIsReady(device);

        TcpipCommand tcpipCommand = new TcpipCommand(device);
        tcpipCommand.exec();
        if (!tcpipCommand.execResult()) {
            System.err.println("tcpip fail: " + tcpipCommand.log());
            return;
        }

        makeSureDeviceIsReady(device);

        DeviceIp deviceIp = getIpCommand.getConnectableDeviceIp();
        if (deviceIp == null) {
            System.err.println("no connectable device ip ");
            return;
        }

        String[] addresses = deviceIp.getAddress().split("/");
        //判断是否为局域网地址
        if (!IpUtil.isIPv4PrivateInternetAddress(addresses[0])) {
            System.err.println("ip is not local address");
            return;
        }

        makeSureDeviceIsReady(device);

        ConnectCommand connectCommand = new ConnectCommand(device, addresses[0], tcpipCommand.getPort());
        connectCommand.exec();
        if (!connectCommand.execResult()) {
            System.err.println("connect device fail: " + connectCommand.log());
            return;
        }

        System.out.println("connected to device: " + device.getSimpleName());
    }

    /**
     * 断开设备连接
     *
     * @param wlanDevice 网络设备
     */
    private void disconnectDevice(Device wlanDevice) {
        DisconnectCommand disconnectCommand = new DisconnectCommand(wlanDevice);
        disconnectCommand.exec();
        if (!disconnectCommand.execResult()) {
            System.out.println("disconnect device: " + wlanDevice.getSimpleName() + " fail");
        }
    }

    /**
     * 从设备中提取当前显示App的apk文件
     */
    private void pullTopApkFromDevice(Device device) {
        GetTopAppInfoCommand getTopAppInfoCommand = new GetTopAppInfoCommand(device).exec();
        if (getTopAppInfoCommand.execResult()) {
            if (getTopAppInfoCommand.isDeviceIsLocked()) {
                System.out.println("device is locked");
                return;
            }

            pullApkFromDevice(device, getTopAppInfoCommand.getTopApp().getPackageName());
        } else {
            System.out.println("get top app fail");
            System.out.println(getTopAppInfoCommand.log());
        }
    }

    /**
     * 从设备中提取apk文件
     */
    private void pullApkFromDevice(Device device, String packageName) {
        if (StringUtil.isTrimEmpty(packageName)) {
            System.out.println("please input packageName");
            return;
        }

        GetPackagePathCommand getPackagePathCommand = new GetPackagePathCommand(device, packageName);
        getPackagePathCommand.exec();

        if (getPackagePathCommand.execResult()) {
            String packagePath = getPackagePathCommand.getPackagePath();

            PullCommand pullCommand = new PullCommand(device, packagePath);
            pullCommand.setOutFileName(packageName + ".apk");
            pullCommand.exec();
            if (pullCommand.execResult()) {
                System.out.println("export apk successful，apkPath: " + pullCommand.getFilePath());

                if (!new OpenDirCommand().exec().execResult()) {
                    System.out.println("open dir fail: " + DirUtil.getCurrentAbsolutePath());
                }
            } else {
                System.out.println(pullCommand.log());
            }
        } else {
            System.out.println(getPackagePathCommand.log());
        }
    }

    private void pushFileToDevice(Device device) {
        
    }

    private void makeSureDeviceIsReady(Device device) throws DeviceNotReadyException {
        for (int i = 0; ; i++) {
            if (i >= 10) {
                throw new DeviceNotReadyException("tcpip fail because no devices found");
            }

            if (isDeviceExist(device)) {
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
    }

    private boolean isDeviceExist(Device device) {
        DevicesCommand devicesCommand = new DevicesCommand();
        devicesCommand.setGetDeviceInfo(false);
        devicesCommand.exec();

        if (devicesCommand.execResult()) {
            for (int i = 0; i < devicesCommand.getDeviceList().size(); i++) {
                if (device.getAdbDeviceName().equals(devicesCommand.getDeviceList().get(i).getAdbDeviceName())) {
                    return true;
                }
            }
        }

        return false;
    }

}
